home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / src / apps / gmemusage / draw.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-08-02  |  30.8 KB  |  1,290 lines

  1. /*
  2.  * draw.c
  3.  *
  4.  * Drawing memory usage
  5.  *
  6.  * Copyright 1994, Silicon Graphics, Inc.
  7.  * All Rights Reserved.
  8.  *
  9.  * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Silicon Graphics, Inc.;
  10.  * the contents of this file may not be disclosed to third parties, copied or
  11.  * duplicated in any form, in whole or in part, without the prior written
  12.  * permission of Silicon Graphics, Inc.
  13.  *
  14.  * RESTRICTED RIGHTS LEGEND:
  15.  * Use, duplication or disclosure by the Government is subject to restrictions
  16.  * as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
  17.  * and Computer Software clause at DFARS 252.227-7013, and/or in similar or
  18.  * successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
  19.  * rights reserved under the Copyright Laws of the United States.
  20.  */
  21.  
  22. #include <gl/gl.h>
  23.  
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <assert.h>
  27. #include <stdio.h>
  28. #include <unistd.h>
  29. #include <bstring.h>
  30.  
  31. #include "process.h"
  32. #include "draw.h"
  33.  
  34. #define FREEPID (-1)         /* smaller than any valid pid */
  35. #define OTHERPID 0x00010000  /* bigger than any valid pid */
  36. #define IRIXPID 0x00010001   /* bigger than OTHERPID */
  37.  
  38. #define FREECOLOR (GREEN + 8)
  39. #define IRIXCOLOR (MAGENTA + 8)
  40. /*
  41.  * We want to use the color (0, 0x24, 0x3f) as the background.  On 24
  42.  * bit systems, we can access this at colormap index 97, but on 8 bit
  43.  * systems we have to map it into the first 16 cells.  8 is one that
  44.  * we're not using.  See code in Init.
  45.  */
  46. #define BGCOLOR 97
  47. #define BGCOLOR_4BIT 8
  48. #define TEXTCOLOR 15
  49.  
  50. #define MARG_NUM 2
  51. #define BAR_NUM 5
  52. #define LABEL_NUM 13
  53. #define LINELEFT_NUM 8
  54. #define LINERIGHT_NUM 12
  55. #define DENOM 26
  56. #define YSHADOWOFF 25
  57. #define XSHADOWOFF 25
  58. #define MINWIDTH 600
  59. #define MINHEIGHT 600
  60. #define TEXTWIDTH 236
  61. #define SLABELWIDTH 88
  62.  
  63. #define MIN(a,b) ((a) < (b) ? (a) : (b))
  64.  
  65. Colorindex barColors[] = { YELLOW + 8, RED + 8, CYAN + 8, BLUE + 8,
  66.                    FREECOLOR, IRIXCOLOR };
  67.  
  68. #define NCOLORS (sizeof barColors/sizeof *barColors - 2)
  69. #define FREEINDEX NCOLORS
  70. #define IRIXINDEX (NCOLORS + 1)
  71.  
  72. static long winWidth, winHeight, barWidth, hmargin, barHeight;
  73. static long topMargin, bottomMargin;
  74. static long labelLeft, lineLeft, lineRight, memThresh;
  75. static long labelTop, labelBottom;
  76. static int textHeight, halfTextHeight;
  77. static Colorindex bgColor;
  78. static otherColor = -1;
  79. static PROGNAME *drawNames;
  80.  
  81. static char *titles[] = {
  82.     "Physical Memory Breakdown",
  83.     "Resident Sizes of Processes",
  84.     "Total Sizes of Processes",
  85.     "Resident Mappings",
  86. };
  87.  
  88. static char *progTitles[] = {
  89.     "Physical Memory use for %s",
  90.     "Resident Size of %s",
  91.     "Total Size of %s",
  92.     "Resident Mapping Breakdown of %s",
  93. };
  94.  
  95. static char *captions[] = {
  96.     "Physical Memory: %d K (%d K used)",
  97.     "Sum of Resident Sizes: %d K",
  98.     "Sum of Total Sizes: %d K",
  99.     "Physical Memory: %d K (%d K used)",
  100. };
  101.  
  102. static char *progCaptions[] = {
  103.     "Physical Memory Use: %d K",
  104.     "Resident Size: %d K",
  105.     "Total Size: %d K",
  106.     "Resident Mapping Breakdown: %d K",
  107. };
  108.  
  109. static char *secondLabels[] = {
  110.     "gmemusage bug!",
  111.     "Private",
  112.     "Shared",
  113.     "Physical",
  114.     "Resident",
  115. };
  116.  
  117. /*
  118.  *  static int
  119.  *  WeWantThisOne(char *progName, long size)
  120.  *
  121.  *  Description:
  122.  *      Given the name of a program and its size, determine whether to
  123.  *      display it separately.  If we return 1, it will get its own
  124.  *      bar; if we return 0, it will get lumped in with a bunch of
  125.  *      others.
  126.  *
  127.  *      The name check occurs if gmemusage was started with the -p
  128.  *      option.  Otherwise, just the threshhold is checked.
  129.  *
  130.  *  Parameters:
  131.  *      progName  Name of program under consideration
  132.  *      size      size of program under consideration
  133.  *
  134.  *  Returns:
  135.  *      1 if this program is to be displayed separately
  136.  *      0 if this program is to be lumped with "other"
  137.  */
  138.  
  139. static int
  140. WeWantThisOne(char *progName, long size)
  141. {
  142.     PROGNAME *name;
  143.  
  144.     if (drawNames) {
  145.     name = drawNames;
  146.  
  147.     while (name) {
  148.         if (strcmp(progName, name->name) == 0) {
  149.         return 1;
  150.         }
  151.         name = name->next;
  152.     }
  153.     return 0;
  154.     }
  155.  
  156.     return size >= memThresh;
  157. }
  158.  
  159. /*
  160.  *  static PROGRAM *
  161.  *  FirstProgram(PROGRAM *prog)
  162.  *
  163.  *  Description:
  164.  *      Find the first non-skip program in a list
  165.  *
  166.  *  Parameters:
  167.  *      prog  list of programs
  168.  *
  169.  *  Returns:
  170.  *      first program in the list that doesn't have the skip flag set
  171.  */
  172.  
  173. static PROGRAM *
  174. FirstProgram(PROGRAM *prog)
  175. {
  176.     while (prog && prog->skip) {
  177.     prog = prog->next;
  178.     }
  179.  
  180.     return prog;
  181. }
  182.  
  183. /*
  184.  *  static PROGRAM *
  185.  *  NextProgram(PROGRAM *prog)
  186.  *
  187.  *  Description:
  188.  *      Find the next non-skip program in a list
  189.  *
  190.  *  Parameters:
  191.  *      prog  list of prograsm
  192.  *
  193.  *  Returns:
  194.  *      the next program in the list that doesn't have the skip
  195.  *      flag set
  196.  */
  197.  
  198. static PROGRAM *
  199. NextProgram(PROGRAM *prog)
  200. {
  201.     if (!prog) {
  202.     return NULL;
  203.     }
  204.  
  205.     do {
  206.     prog = prog->next;
  207.     } while (prog && prog->skip);
  208.  
  209.     return prog;
  210. }
  211.  
  212. /*
  213.  *  static PROGRAM *
  214.  *  PrevProgram(PROGRAM *prog)
  215.  *
  216.  *  Description:
  217.  *      Find the previous non-skip rogram in a list
  218.  *
  219.  *  Parameters:
  220.  *      prog   list of programs
  221.  *
  222.  *  Returns:
  223.  *      The previous program in the list that doesn't have the skip
  224.  *      flag set.
  225.  */
  226.  
  227. static PROGRAM *
  228. PrevProgram(PROGRAM *prog)
  229. {
  230.     if (!prog) {
  231.     return NULL;
  232.     }
  233.  
  234.     do {
  235.     prog = prog->prev;
  236.     } while (prog && prog->skip);
  237.  
  238.     return prog;
  239. }
  240.  
  241. /*
  242.  *  PROGRAM *
  243.  *  DrawSetup(PROGRAM *new, PROGRAM *old, long physMem, long freeMem,
  244.  *            UsageType type, SecondType stype, int all, int *barTotal,
  245.  *            int *numBars)
  246.  *
  247.  *  Description:
  248.  *      Add a bar for free memory (Physical).  Allocate colors
  249.  *      for each of the bars, add a bar for "other", and add a bar for
  250.  *      Irix (Physical).
  251.  *
  252.  *  Parameters:
  253.  *      new      The new list of programs to display as bars
  254.  *      old      The last one displayed; we use this to keep the
  255.  *               colors consistent
  256.  *      physMem  Amount of physical memory on the system
  257.  *      freeMem  Amount of memory the kernel says is free
  258.  *      type     Type of bars to display
  259.  *      stype    Secondary type of bars to display
  260.  *      all      1 if this is all processes, 0 if this is a breakdown
  261.  *               for one process
  262.  *      barTotal gets total for the bar
  263.  *    numBars  gets number of bars
  264.  *
  265.  *  Returns:
  266.  *      The list of programs to display (same as new with some stuff
  267.  *      added)
  268.  */
  269.  
  270. PROGRAM *
  271. DrawSetup(PROGRAM *new, PROGRAM *old, long physMem, long freeMem,
  272.       UsageType type, SecondType stype, int all, int *barTotal,
  273.       int *numBars)
  274. {
  275.     PROGRAM *prev = NULL, *prevColor = NULL, *head;
  276.     long progTotal, otherMem = 0, userMem, otherSecondVal = 0;
  277.     char otherBuf[100];
  278.     int nBars;
  279.     
  280.     nBars = 0;
  281.     
  282.     progTotal = 0;
  283.     
  284.     if (type == Physical || type == MappedObjects) {
  285.     if (all) {
  286.         *barTotal = physMem;
  287.         /*
  288.          * Add the free chunk at the top
  289.          */
  290.         head = malloc(sizeof *head);
  291.         /*
  292.          * Be very, very careful here.  FreeUsage will free
  293.          * progName and mapName, but not manType!!
  294.          */
  295.         head->progName = strdup("Free");
  296.         head->mapName = strdup("Free");
  297.         head->mapType = "Free";
  298.         head->value = freeMem;
  299.         head->size = head->resSize = head->weightSize = head->privSize
  300.         = head->value;
  301.         head->secondValue = NOSVAL;
  302.         head->color = FREEINDEX;
  303.         head->pid = FREEPID;
  304.         head->nProc = 1;
  305.         head->skip = 0;
  306.         head->special = 1;
  307.         head->print = 1;
  308.         nBars++;
  309.         
  310.         head->prev = NULL;
  311.         head->next = new;
  312.         new->prev = head;
  313.     } else {
  314.         head = new;
  315.     }
  316.     /*
  317.      * Add up all the sizes
  318.      */
  319.     while (new) {
  320.         progTotal += new->weightSize;
  321.         new = new->next;
  322.     }
  323.     if (!all) {
  324.         *barTotal = progTotal;
  325.     }
  326.     } else {
  327.     head = new;
  328.     while (new) {
  329.         progTotal += type == Resident ? new->resSize : new->size;
  330.         new = new->next;
  331.     }
  332.     *barTotal = progTotal;
  333.     }
  334.     
  335.     userMem = progTotal;
  336.     new = (type == Physical || type == MappedObjects)
  337.     && all ? head->next : head;
  338.  
  339.     while (new) {
  340.     /*
  341.      * Set the values for each bar appropriately
  342.      */
  343.     switch (type) {
  344.     case Physical:
  345.     case MappedObjects:
  346.         new->value = new->weightSize;
  347.         break;
  348.     case Resident:
  349.         new->value = new->resSize;
  350.         break;
  351.     default:
  352.     case Size:
  353.         new->value = new->size;
  354.         break;
  355.     }
  356.  
  357.     switch (stype) {
  358.     default:
  359.     case None:
  360.         new->secondValue = NOSVAL;
  361.         break;
  362.     case Priv:
  363.         new->secondValue = new->privSize;
  364.         break;
  365.     case Shared:
  366.         new->secondValue = new->weightSize - new->privSize;
  367.         break;
  368.     case Phys:
  369.         new->secondValue = new->weightSize;
  370.         break;
  371.     case Res:
  372.         new->secondValue = new->resSize;
  373.         break;
  374.     }
  375.  
  376.     /*
  377.      * Set the skip flag, allocate colors (using the old list if
  378.      * it's valid), and add up the amount to be displayed in the
  379.      * "other" bar.
  380.      */
  381.     if (!all || WeWantThisOne(new->progName, new->value)) {
  382.         new->skip = 0;
  383.         new->special = 0;
  384.         while (old && old->pid < new->pid) {
  385.         old = old->next;
  386.         }
  387.         
  388.         if (old && !old->skip && old->pid == new->pid) {
  389.         new->color = old->color;
  390.         } else if (prevColor) {
  391.         new->color = (prevColor->color + 1) % NCOLORS;
  392.         } else {
  393.         new->color = 0;
  394.         }
  395.         prevColor = new;
  396.         nBars++;
  397.     } else {
  398.         new->skip = 1;
  399.         otherMem += new->value;
  400.         otherSecondVal += new->secondValue;
  401.     }
  402.  
  403.     prev = new;
  404.     new = new->next;
  405.     }
  406.  
  407.     /*
  408.      * Add the other bar
  409.      */
  410.     if (otherMem) {
  411.     /*
  412.      * The check for old resets other color if the rest of the
  413.      * bars are also getting reset
  414.      */
  415.     if (otherColor == -1 || !old) {
  416.         otherColor = prevColor ? (prevColor->color + 1) % NCOLORS : 0;
  417.     }
  418.     new = malloc(sizeof *new);
  419.     if (!drawNames) {
  420.         sprintf(otherBuf, "< %d", memThresh);
  421.     }        
  422.     new->progName = strdup(drawNames ? "Other" : otherBuf);
  423.     new->mapName = NULL;
  424.     new->value = otherMem;
  425.     new->secondValue = otherSecondVal;
  426.     new->pid = OTHERPID;
  427.     new->color = otherColor;
  428.     new->next = NULL;
  429.     new->prev = prev;
  430.     new->skip = 0;
  431.     new->special = 1;
  432.     new->print = 0;
  433.     new->nProc = 1;
  434.     prev->next = new;
  435.     prev = new;
  436.     nBars++;
  437.     }
  438.  
  439.     if ((type == Physical || type == MappedObjects) && all) {
  440.     /*
  441.      * Add the Irix chunk at the bottom
  442.      */
  443.     if (prev) {
  444.         new = malloc(sizeof *new);
  445.         /*
  446.          * Be very, very careful here.  FreeUsage will free
  447.          * progName and mapName, but not manType!!
  448.          */
  449.         new->progName = strdup(IRIX);
  450.         new->mapName = strdup(IRIX);
  451.         new->mapType = IRIX;
  452.         new->value = physMem - userMem - freeMem;
  453.         new->size = new->resSize = new->weightSize = new->privSize
  454.         = new->value;
  455.         new->secondValue = NOSVAL;
  456.         new->color = IRIXINDEX;
  457.         new->pid = IRIXPID;
  458.         new->nProc = 1;
  459.         new->next = NULL;
  460.         new->prev = prev;
  461.         new->skip = 0;
  462.         new->special = 0;
  463.         new->print = 1;
  464.         prev->next = new;
  465.         nBars++;
  466.     }
  467.     }
  468.  
  469.     *numBars = nBars;
  470.     return head;
  471. }
  472.  
  473. /*
  474.  *  void
  475.  *  Resize(void)
  476.  *
  477.  *  Description:
  478.  *      This sets some global variables based on the size of the window.
  479.  */
  480.  
  481. void
  482. Resize(void)
  483. {
  484.     reshapeviewport();
  485.     getsize(&winWidth, &winHeight);
  486.     ortho2(0, winWidth, 0, winHeight);
  487.  
  488.     hmargin = (MARG_NUM * winWidth) / DENOM;
  489.     barWidth = (BAR_NUM * winWidth) / DENOM;
  490.     textHeight = getheight();
  491.     halfTextHeight = textHeight / 2;
  492.  
  493.     if (hmargin * 3 < winHeight) {
  494.     topMargin = hmargin;
  495.     bottomMargin = (hmargin * 3) / 2;
  496.     barHeight = winHeight - topMargin - bottomMargin;
  497.     } else {
  498.     barHeight = winHeight;
  499.     topMargin = bottomMargin = 0;
  500.     }
  501.     labelLeft = winWidth - TEXTWIDTH - SLABELWIDTH;
  502.     lineLeft = (winWidth * LINELEFT_NUM) / DENOM;
  503.     lineRight = labelLeft - winWidth / DENOM;
  504.     labelTop = winHeight - topMargin;
  505.     labelBottom = bottomMargin;
  506. }
  507.  
  508. /*
  509.  *  void
  510.  *  Init(PROGNAME *progNames, long threshHold)
  511.  *
  512.  *  Description:
  513.  *      Initialize.  Call winopen, and call Resize to set global
  514.  *      variables.
  515.  *
  516.  *  Parameters:
  517.  *      progNames   List of program names given in -p option
  518.  *      threshHold  the initial threshhold to use (can be changed
  519.  *                  later by SetThreshHold)
  520.  */
  521.  
  522. void
  523. Init(PROGNAME *progNames, long threshHold)
  524. {
  525.     char hname[50], title[100];
  526.  
  527.     if (gethostname(hname, sizeof hname) < 0) {
  528.     strcpy(title, "Memory Usage");
  529.     } else {
  530.     sprintf(title, "Memory Usage @ %s", hname);
  531.     }
  532.     
  533.     minsize(MINWIDTH, MINHEIGHT);
  534.     maxsize(MINWIDTH, getgdesc(GD_XPMAX));
  535.     winopen("gmemusage");
  536.     wintitle(title);
  537.     icontitle("gmemusage");
  538.     doublebuffer();
  539.     gconfig();
  540.     Resize();
  541.  
  542.     bgColor = getplanes() < 8 ? BGCOLOR_4BIT : BGCOLOR;
  543.  
  544.     mapcolor(bgColor, 0, 0x24, 0x3f);
  545.  
  546.     color(bgColor);
  547.     clear();
  548.     swapbuffers();
  549.     gflush();
  550.     /*
  551.      * This makes the outlines of the bars match up with the bars
  552.      * themselves.
  553.      */
  554.     glcompat(GLC_OLDPOLYGON, 0);
  555.     memThresh = threshHold;
  556.     drawNames = progNames;
  557. }
  558.  
  559. /*
  560.  *  static void
  561.  *  DrawBar(long top, long height, Colorindex barColor, long margin,
  562.  *          long width, long split)
  563.  *
  564.  *  Description:
  565.  *      Draw one of the memory bars
  566.  *
  567.  *  Parameters:
  568.  *      top       The top of the bar
  569.  *      height    The height of the bar
  570.  *      barColor  The color of the bar
  571.  *    margin    left side of bar
  572.  *      width     width of bar
  573.  *    long      split
  574.  */
  575.  
  576. static void
  577. DrawBar(long top, long height, Colorindex barColor, long margin,
  578.     long width, long split)
  579. {
  580.     color(barColor);
  581.     rectfi(margin, top - height, margin + width, top);
  582.     color(BLACK);
  583.     recti(margin, top, margin + width, top - height);
  584.  
  585.     if (split) {
  586.     recti(margin + width - split, top, margin + width, top - height);
  587.     }
  588. }
  589.  
  590. /*
  591.  *  static void
  592.  *  SetupLabels(PROGRAM *usageList, int numBars)
  593.  *
  594.  *  Description:
  595.  *      Set the labelOffset member of each non-skip element in the
  596.  *      list.  This is kind of tricky.  We want each label to be as
  597.  *      close as possible (vertically) to the center of the bar to
  598.  *      which it corresponds, while at the same time preventing the
  599.  *      labels from overlapping.
  600.  *
  601.  *      If there's no extra room, meaning that there are more labels
  602.  *      than can fit in the space provided, we spread them evenly to
  603.  *      minimize overlap.
  604.  *
  605.  *      Otherwise, we know that all the labels can fit without
  606.  *      overlapping, but there may be clusters of labels that are too
  607.  *      close together.  So we locate these clusters, and try to center
  608.  *      them with respect to the bars to which they correspond.  We
  609.  *      check how much extra space is above and below the cluster, and
  610.  *      skew either way if there isn't enough for us to center
  611.  *      ourselves.  Furthermore, once we've set up a cluster we have
  612.  *      to go back up the list, pushing the previous labels up if our
  613.  *      new cluster location overlaps an old cluster location.
  614.  *
  615.  *      The reason that all this works is that we have predetermined
  616.  *      that there really is enough space to do this; we always know
  617.  *      that our cluster will fit one way or the other, and we won't
  618.  *      push labels up off the top of the window because we checked
  619.  *      the space first and didn't assume there was more space above
  620.  *      us than there actually is.
  621.  *
  622.  *  Parameters:
  623.  *      usageList  list of labels to spread out
  624.  *      numBars    number of bars in the list
  625.  */
  626.  
  627. static void
  628. SetupLabels(PROGRAM *usageList, int numBars)
  629. {
  630.     int offset;
  631.     float aveSep, foffset;
  632.     int nLabels, top, bottom, extraAbove, extraBelow;
  633.     int nCluster, center, firstCenter, lastCenter;
  634.     PROGRAM *usage, *next, *prev, *elem;
  635.  
  636.     aveSep = (numBars > 1) ?
  637.     (float)(labelTop - labelBottom) / (float)(numBars - 1)
  638.         : labelTop - labelBottom;
  639.  
  640.     /*
  641.      * Spread 'em evenly if there's no extra space.  Do the
  642.      * calculations in floating point so we utilize the extra space as
  643.      * well as we can.
  644.      */
  645.     if (aveSep <= textHeight) {
  646.     usage = usageList;
  647.     foffset = labelTop;
  648.     while (usage) {
  649.         if (!usage->skip) {
  650.         usage->labelOffset = foffset;
  651.         foffset -= aveSep;
  652.         }
  653.         usage = usage->next;
  654.     }
  655.     return;
  656.     }
  657.  
  658.     /*
  659.      * If we got here, then there's enough space to have at least
  660.      * textHeight space between each pair of labels.  We try our best
  661.      * to keep the labels close vertically to the center of the bars
  662.      * to which they correspond.
  663.      */
  664.     usage = FirstProgram(usageList);
  665.     nLabels = 0;
  666.  
  667.     while (usage) {
  668.     /*
  669.      * Find the extent of this cluster.  We consider single labels
  670.      * that have lots of space on either side to be clusters of
  671.      * size one.
  672.      */
  673.     firstCenter = usage->center;
  674.     bottom = firstCenter + halfTextHeight;
  675.     next = NextProgram(usage);
  676.     nCluster = 1;
  677.     lastCenter = usage->center;
  678.     center = usage->center;
  679.     bottom = usage->center - textHeight;
  680.  
  681.     while (next && bottom <= next->center) {
  682.         nCluster++;
  683.         lastCenter = next->center;
  684.         center = lastCenter + (firstCenter - lastCenter) / 2;
  685.         bottom = center - (nCluster * textHeight) / 2;
  686.         next = NextProgram(next);
  687.     }
  688.  
  689.     /*
  690.      * bottom and center have been set below; now figure out where
  691.      * the top is.
  692.      */
  693.     top = center + ((nCluster - 1) * textHeight + 1) / 2;
  694.  
  695.     /*
  696.      * We want to have this cluster extend from top to bottom.
  697.      * Now we calculate how much extra space there is above and
  698.      * below us, and skew the cluster down if there's not enough
  699.      * space above us and up if there's not enough space below us.
  700.      *
  701.      * Remember, there's guaranteed to be enough space *somewhere*
  702.      * for this cluster to fit.
  703.      */
  704.     extraAbove = labelTop - top - textHeight * nLabels;
  705.     extraBelow = bottom - labelBottom - textHeight *
  706.         (numBars - nLabels - nCluster);
  707.  
  708.     if (extraAbove < 0) {
  709.         top += extraAbove;
  710.     } else if (extraBelow < 0) {
  711.         top -= extraBelow;
  712.     }
  713.  
  714.     nLabels += nCluster;
  715.  
  716.     elem = usage;
  717.     offset = top;
  718.     
  719.     /*
  720.      * Set the labelOffsets.  The members of the cluster will be
  721.      * exactly textHeight away from one another.
  722.      */
  723.     while (nCluster--) {
  724.         elem->labelOffset = offset;
  725.         offset -= textHeight;
  726.         elem = NextProgram(elem);
  727.     }
  728.     
  729.     /*
  730.      * Remember where we were for the next iteration
  731.      */
  732.     next = elem;
  733.  
  734.     /*
  735.      * Push previous labels up if necessary
  736.      */
  737.     elem = usage;
  738.     prev = PrevProgram(usage);
  739.     while (prev &&
  740.            prev->labelOffset - elem->labelOffset < textHeight) {
  741.         prev->labelOffset = elem->labelOffset + textHeight;
  742.         elem = prev;
  743.         prev = PrevProgram(elem);
  744.     }
  745.  
  746.     usage = next;
  747.     }
  748. }
  749.  
  750. /*
  751.  *  void
  752.  *  DrawLabels(PROGRAM *usageList, SecondType stype)
  753.  *
  754.  *  Description:
  755.  *      Draw the labels down the right side of the window
  756.  *
  757.  *  Parameters:
  758.  *      usageList  list of bars and labels
  759.  *    stype      type of secondary labels
  760.  */
  761.  
  762. void
  763. DrawLabels(PROGRAM *usageList, SecondType stype)
  764. {
  765.     PROGRAM *usage = usageList;
  766.     char labelBuf[100];
  767.  
  768.     while (usage) {
  769.     if (!usage->skip) {
  770.         color(barColors[usage->color]);
  771.         /*
  772.          * If there is more than one copy of a program running,
  773.          * denote that with the number of copies running in
  774.          * parentheses.
  775.          */
  776.         if (usage->nProc == 1) {
  777.         sprintf(labelBuf, "%-16.16s: %d K", usage->progName,
  778.             usage->value);
  779.         } else if (usage->nProc < 10) {
  780.         sprintf(labelBuf, "%-12.12s (%d): %d K", usage->progName,
  781.             usage->nProc, usage->value);
  782.         } else {
  783.         sprintf(labelBuf, "%-11.11s (%d): %d K", usage->progName,
  784.             usage->nProc, usage->value);
  785.         }
  786.         /*
  787.          * For some reason, halfTextHeight / 2 looks really nice.
  788.          */
  789.         cmov2i(labelLeft, usage->labelOffset - halfTextHeight / 2);
  790.         charstr(labelBuf);
  791.  
  792.         if (stype != None && usage->secondValue != NOSVAL) {
  793.         sprintf(labelBuf, "%d K", usage->secondValue);
  794.         cmov2i(winWidth - SLABELWIDTH,
  795.                usage->labelOffset - halfTextHeight / 2);
  796.         charstr(labelBuf);
  797.         }
  798.     }
  799.     usage = usage->next;
  800.     }
  801. }
  802.  
  803. /*
  804.  *  void
  805.  *  DrawLines(PROGRAM *usage)
  806.  *
  807.  *  Description:
  808.  *      Draw the lines from the bars to the labels
  809.  *
  810.  *  Parameters:
  811.  *      usage  List of labels and bars
  812.  */
  813.  
  814. void
  815. DrawLines(PROGRAM *usage)
  816. {
  817.     long pt[2];
  818.     while (usage) {
  819.     if (!usage->skip) {
  820.         color(barColors[usage->color]);
  821.         bgnline();
  822.         pt[0] = lineLeft;
  823.         pt[1] = usage->center;
  824.         v2i(pt);
  825.         pt[0] = lineRight;
  826.         pt[1] = usage->labelOffset /* - halfTextHeight */;
  827.         v2i(pt);
  828.         endline();
  829.     }
  830.     usage = usage->next;
  831.     }
  832. }
  833.  
  834. /*
  835.  *  void
  836.  *  Draw(PROGRAM *usageList, int barTotal, int numBars, char *progName,
  837.  *       UsageType type)
  838.  *
  839.  *  Description:
  840.  *      Draw captions, bars, labels, and lines
  841.  *
  842.  *  Parameters:
  843.  *      usageList  bars and labels to draw
  844.  *      barTotal   total memory used by bar
  845.  *      numBars    number of bars
  846.  *      progName   name of program (if this is memory use within a program)
  847.  *      type       type of chart we're drawing.  Used to select the
  848.  *                 proper captions
  849.  *    stype       secondary type of chart, for more captions and labels
  850.  */
  851.  
  852. void
  853. Draw(PROGRAM *usageList, int barTotal, int numBars, char *progName,
  854.      UsageType type, SecondType stype)
  855. {
  856.     long top, height, split;
  857.     int swidth, secondValTotal = 0;
  858.     char buf[100], *str;
  859.     PROGRAM *usage;
  860.     
  861.     if (!progName) {
  862.     color(bgColor);
  863.     clear();
  864.     }
  865.  
  866.     /*
  867.      * Draw a title based on the type of graph
  868.      */
  869.     color(TEXTCOLOR);
  870.     if (progName) {
  871.     sprintf(buf, progTitles[type], progName);
  872.     str = buf;
  873.     } else {
  874.     str = titles[type];
  875.     }
  876.     swidth = strwidth(str);
  877.     cmov2i(winWidth / 2 - swidth / 2,
  878.        winHeight - topMargin / 2 - halfTextHeight);
  879.     charstr(str);
  880.  
  881.     /*
  882.      * Draw the bars
  883.      */
  884.     top = winHeight - topMargin;
  885.  
  886.     usage = usageList;
  887.  
  888.     while (usage) {
  889.     if (!usage->skip) {
  890.         height = (usage->value * barHeight) / barTotal;
  891.         split = usage->value > 0 ?
  892.         (usage->secondValue * barWidth) / usage->value : 0;
  893.         if (split > barWidth || split < 0) {
  894.         split = 0;
  895.         }
  896.         DrawBar(top, height, barColors[usage->color],
  897.             hmargin, barWidth, split);
  898.         usage->top = top;
  899.         usage->height = height;
  900.         usage->center = top - height / 2;
  901.         top -= height;
  902.         secondValTotal += usage->secondValue;
  903.     }
  904.     usage = usage->next;
  905.     }
  906.  
  907.     color(TEXTCOLOR);
  908.     /*
  909.      * Draw the total
  910.      */
  911.     if (!progName && (type == Physical || type == MappedObjects)) {
  912.     sprintf(buf, captions[type], barTotal, barTotal - usageList->value);
  913.     } else {
  914.     sprintf(buf, (progName ? progCaptions : captions)[type], barTotal);
  915.     }
  916.     swidth = strwidth(buf);
  917.     cmov2i(winWidth / 2 - swidth / 2, textHeight * 2 + halfTextHeight);
  918.     charstr(buf);
  919.     
  920.     /*
  921.      * Tell the user how to get help
  922.      */
  923.     str = "Press 'h' for help.";
  924.     swidth = strwidth(str);
  925.     cmov2i(winWidth / 2 - swidth / 2, textHeight);
  926.     charstr(str);
  927.  
  928.     /*
  929.      * Draw the labels and lines
  930.      */
  931.     SetupLabels(usageList, numBars);
  932.     DrawLabels(usageList, stype);
  933.     DrawLines(usageList);
  934.  
  935.     if (stype != None) {
  936.     color(TEXTCOLOR);
  937.     cmov2i(winWidth - SLABELWIDTH,
  938.            winHeight - topMargin / 2 - halfTextHeight);
  939.     charstr(secondLabels[stype]);
  940.  
  941.     cmov2i(winWidth - SLABELWIDTH,
  942.            textHeight * 2 + halfTextHeight);
  943.     sprintf(buf, "%d K", secondValTotal);
  944.     charstr(buf);
  945.     }
  946.  
  947.     swapbuffers();
  948.     gflush();
  949. }
  950.  
  951. /*
  952.  *  void
  953.  *  DrawShadow(PROGRAM *usage, int barTotal, char *progName)
  954.  *
  955.  *  Description:
  956.  *      Draw shadow bars, so the user can tell how a program's memory
  957.  *      use breakdown fits in with the rest of memory.  The current
  958.  *      program is drawn solid, the rest are just outlines.
  959.  *
  960.  *  Parameters:
  961.  *      usage     shadow usage
  962.  *      barTotal  total of the bars
  963.  *      progName  name of the program to draw solid.
  964.  */
  965.  
  966. void
  967. DrawShadow(PROGRAM *usage, int barTotal, char *progName)
  968. {
  969.     long top, height, left;
  970.  
  971.     color(bgColor);
  972.     clear();
  973.  
  974.     color(TEXTCOLOR);
  975.  
  976.     top = winHeight - topMargin - YSHADOWOFF;
  977.     left = hmargin - XSHADOWOFF;
  978.  
  979.     while (usage) {
  980.     if (!usage->skip) {
  981.         height = (usage->value * barHeight) / barTotal;
  982.         (strcmp(usage->progName, progName) == 0 ?
  983.          rectfi : recti)(left, top, left + barWidth, top - height);
  984.         usage->top = top;
  985.         usage->height = height;
  986.         top -= height;
  987.     }
  988.     usage = usage->next;
  989.     }
  990.  
  991.     /*
  992.      * Don't swapbuffers or gflush here; we're depending on a
  993.      * subsequent call to Draw for that.
  994.      */
  995. }
  996.     
  997.  
  998. /*
  999.  *  char *
  1000.  *  Select(PROGRAM *usage, long x, long y, int *procMode, int dragging)
  1001.  *
  1002.  *  Description:
  1003.  *      The user has clicked the mouse.  Figure out what he or she has
  1004.  *      selected.
  1005.  *
  1006.  *    This function helps implement the policy of what kind of
  1007.  *    display to do by modifying the procMode parameter.  What we're
  1008.  *    implementing is that if we're in procMode and the user clicks
  1009.  *    anywhere outside the shadow bar, we should go back to regular
  1010.  *    mode.  This may be more convenient than using the space bar,
  1011.  *    which is the "official" means of return.
  1012.  *
  1013.  *  Parameters:
  1014.  *      usage      List of usage
  1015.  *      x          x coordinate of mouse click
  1016.  *      y          y coordinate of mouse click
  1017.  *      procMode   1 if we're in procMode, 0 otherwise
  1018.  *      dragging   1 if we're dragging, 0 otherwise
  1019.  *
  1020.  *  Returns:
  1021.  *      name of the thing that was clicked on
  1022.  */
  1023.  
  1024. char *
  1025. Select(PROGRAM *usage, long x, long y, int *procMode, int dragging)
  1026. {
  1027.     if (*procMode) {
  1028.     if (y < bottomMargin - YSHADOWOFF
  1029.         || y > winHeight - topMargin - YSHADOWOFF) {
  1030.         if (!dragging) {
  1031.         *procMode = 0;
  1032.         }
  1033.         return NULL;
  1034.     }
  1035.         
  1036.     /*
  1037.      * Adjust x coordinate for procMode.  The bars will all have
  1038.      * the correct top and height for comparison purposes because
  1039.      * these got calculated in DrawShadow, but the x has to be
  1040.      * adjusted because the comparison code below assumes the bars
  1041.      * are drawn in the normal place instead of offset to the
  1042.      * left.
  1043.      */
  1044.     x += XSHADOWOFF;
  1045.     }
  1046.  
  1047.     if (hmargin <= x && x <= hmargin + barWidth) {
  1048.     while (usage) {
  1049.         if (!usage->skip && !usage->special && y <= usage->top
  1050.         && y > usage->top - usage->height) {
  1051.         return strdup(usage->progName);
  1052.         }
  1053.         usage = usage->next;
  1054.     }
  1055.     } else if (!*procMode && labelLeft <= x && x <= winWidth - hmargin) {
  1056.     while (usage) {
  1057.         if (!usage->skip && !usage->special && y <=
  1058.         usage->labelOffset + halfTextHeight &&
  1059.         y > usage->labelOffset - halfTextHeight) {
  1060.         return strdup(usage->progName);
  1061.         }
  1062.         usage = usage->next;
  1063.     }
  1064.     } else if (!dragging) {
  1065.     *procMode = 0;
  1066.     }
  1067.  
  1068.     return NULL;
  1069. }
  1070.  
  1071. /*
  1072.  *  void
  1073.  *  Help(void)
  1074.  *
  1075.  *  Description:
  1076.  *      Display some (hopefully) useful information on running
  1077.  *      gmemusage.
  1078.  */
  1079.  
  1080. void
  1081. Help(void)
  1082. {
  1083.     char *str;
  1084.     int swidth, tabStop;
  1085.     long textY;
  1086.  
  1087.     color(bgColor);
  1088.     clear();
  1089.  
  1090.     color(TEXTCOLOR);
  1091.     textY = winHeight - textHeight - halfTextHeight;
  1092.  
  1093.     str = "Command line usage:";
  1094.     swidth = strwidth(str);
  1095.     cmov2i(MINWIDTH / 2 - swidth / 2, textY);
  1096.     charstr(str);
  1097.  
  1098.     textY -= textHeight + halfTextHeight;
  1099.  
  1100.     tabStop = strwidth("gmemusage ");
  1101.  
  1102.     str = "gmemusage [ -i interval ] [ -m | -p | -r | -s ] [ -u ]";
  1103.     cmov2i(hmargin, textY);
  1104.     charstr(str);
  1105.     textY -= textHeight;
  1106.  
  1107.     cmov2i(hmargin + tabStop, textY);
  1108.     charstr("[ -a aiff-file ] [ -g growth-threshhold ]");
  1109.     textY -= textHeight;
  1110.  
  1111.     cmov2i(hmargin + tabStop, textY);
  1112.     charstr("[ -t thresh ] [ -d delta ] [ progs ... ]");
  1113.     textY -= textHeight + halfTextHeight;
  1114.  
  1115.     cmov2i(hmargin, textY);
  1116.     charstr("interval is the polling interval in milliseconds");
  1117.     textY -= textHeight + halfTextHeight;
  1118.  
  1119.     cmov2i(hmargin, textY);
  1120.     charstr("-m selects Resident Mappings");
  1121.     textY -= textHeight;
  1122.  
  1123.     cmov2i(hmargin, textY);
  1124.     charstr("-p selects Physical Memory Breakdown (default)");
  1125.     textY -= textHeight;
  1126.  
  1127.     cmov2i(hmargin, textY);
  1128.     charstr("-r selects Resident Sizes of Processes");
  1129.     textY -= textHeight;
  1130.  
  1131.     cmov2i(hmargin, textY);
  1132.     charstr("-s selects Total Sizes of Processes");
  1133.     textY -= textHeight + halfTextHeight;
  1134.  
  1135.     cmov2i(hmargin, textY);
  1136.     charstr("-u forces update of inode lookup table");
  1137.     textY -= textHeight + halfTextHeight;
  1138.  
  1139.     cmov2i(hmargin, textY);
  1140.     charstr("aiff-file is the sound for program usage");
  1141.     textY -= textHeight;
  1142.  
  1143.     cmov2i(hmargin, textY);
  1144.     charstr("growth-threshhold is the minimum increase for usage sounds");
  1145.     textY -= textHeight + halfTextHeight;
  1146.  
  1147.     cmov2i(hmargin, textY);
  1148.     charstr("thresh is the memory threshhold for displaying");
  1149.     textY -= textHeight;
  1150.  
  1151.     cmov2i(hmargin, textY);
  1152.     charstr("individual programs (defaults to 500 K),");
  1153.     textY -= textHeight;
  1154.  
  1155.     cmov2i(hmargin, textY);
  1156.     charstr("delta is the amount by which the arrow keys");
  1157.     textY -= textHeight;
  1158.  
  1159.     cmov2i(hmargin, textY);
  1160.     charstr("change the threshhold (defaults to 50 K), and");
  1161.     textY -= textHeight;
  1162.  
  1163.     cmov2i(hmargin, textY);
  1164.     charstr("progs is a list of programs to view.");
  1165.     textY -= textHeight * 2;
  1166.  
  1167.     str = "Runtime usage:";
  1168.     swidth = strwidth(str);
  1169.     cmov2i(MINWIDTH / 2 - swidth / 2, textY);
  1170.     charstr(str);
  1171.     textY -= textHeight + halfTextHeight;
  1172.  
  1173.     cmov2i(hmargin, textY);
  1174.     charstr("Click on a bar to view detailed memory usage,");
  1175.     textY -= textHeight;
  1176.  
  1177.     cmov2i(hmargin, textY);
  1178.     charstr("and from there press the space bar to return");
  1179.     textY -= textHeight;
  1180.  
  1181.     cmov2i(hmargin, textY);
  1182.     charstr("to the overview.");
  1183.     textY -= textHeight + halfTextHeight;
  1184.  
  1185.     cmov2i(hmargin, textY);
  1186.     charstr("'m' selects Resident Mappings");
  1187.     textY -= textHeight;
  1188.  
  1189.     cmov2i(hmargin, textY);
  1190.     charstr("'p' selects Physical Memory Breakdown");
  1191.     textY -= textHeight;
  1192.  
  1193.     cmov2i(hmargin, textY);
  1194.     charstr("'r' selects Resident Sizes of Processes");
  1195.     textY -= textHeight;
  1196.  
  1197.     cmov2i(hmargin, textY);
  1198.     charstr("'s' selects Total Sizes of Processes");
  1199.     textY -= textHeight;
  1200.  
  1201.     cmov2i(hmargin, textY);
  1202.     charstr("'v' cycles through some interesting information");
  1203.     textY -= textHeight;
  1204.  
  1205.     cmov2i(hmargin, textY);
  1206.     charstr("'t' prints current information to stdout");
  1207.     textY -= textHeight + halfTextHeight;
  1208.  
  1209.     cmov2i(hmargin, textY);
  1210.     charstr("up arrow key increases the threshhold");
  1211.     textY -= textHeight;
  1212.  
  1213.     cmov2i(hmargin, textY);
  1214.     charstr("down arrow key decreases the threshhold");
  1215.     textY -= textHeight + halfTextHeight;
  1216.  
  1217.     cmov2i(hmargin, textY);
  1218.     charstr("escape key exits");
  1219.     textY -= textHeight * 3;
  1220.  
  1221.     str = "See also the gmemusage(1) man page";
  1222.     swidth = strwidth(str);
  1223.     cmov2i(MINWIDTH / 2 - swidth / 2, textHeight * 2 + halfTextHeight);
  1224.     charstr(str);
  1225.  
  1226.     str = "Press the space bar to return to Usage Viewer";
  1227.     swidth = strwidth(str);
  1228.     cmov2i(MINWIDTH / 2 - swidth / 2, textHeight);
  1229.     charstr(str);
  1230.  
  1231.     swapbuffers();
  1232.     gflush();
  1233. }
  1234.  
  1235. /*
  1236.  *  void
  1237.  *  SetThreshHold(long thresh)
  1238.  *
  1239.  *  Description:
  1240.  *      Specify a new threshhold
  1241.  *
  1242.  *  Parameters:
  1243.  *      thresh  the new threshhold
  1244.  */
  1245.  
  1246. void
  1247. SetThreshHold(long thresh)
  1248. {
  1249.     memThresh = thresh;
  1250. }
  1251.  
  1252. /*
  1253.  *  void
  1254.  *  WaitMessage(char *message)
  1255.  *
  1256.  *  Description:
  1257.  *      Display a wait message instead of bars
  1258.  *
  1259.  *  Parameters:
  1260.  *      message   the message to display
  1261.  *      detail    another line to display, if not NULL
  1262.  */
  1263.  
  1264. void
  1265. WaitMessage(char *message, char *detail)
  1266. {
  1267.     int swidth;
  1268.     int textY;
  1269.  
  1270.     color(bgColor);
  1271.     clear();
  1272.  
  1273.     color(TEXTCOLOR);
  1274.     textY = winHeight / 2 + halfTextHeight;
  1275.  
  1276.     swidth = strwidth(message);
  1277.     cmov2i(winWidth / 2 - swidth / 2, textY);
  1278.     charstr(message);
  1279.     textY -= textHeight * 2;
  1280.     
  1281.     if (detail) {
  1282.     swidth = strwidth(detail);
  1283.     cmov2i(winWidth / 2 - swidth / 2, textY);
  1284.     charstr(detail);
  1285.     }
  1286.  
  1287.     swapbuffers();
  1288.     gflush();
  1289. }
  1290.